
#include <errno.h>
#include <sys/mman.h>
#include "BufChunkMgr.h"
#include "MemPool.h"

// 4M: 4×1024×1024
#define CHUNK_SIZE 4194304

CBufChunkMgr::CBufChunkMgr()
{
}

CBufChunkMgr::~CBufChunkMgr()
{
    Destroy();
}

void *CBufChunkMgr::operator new(size_t sz)
{
    CMemPool *mp = CMemPool::GetInstance();

    if (nullptr == mp)
        return NULL;

    return mp->Alloc(sz);
}

void CBufChunkMgr::operator delete(void *ptr)
{
    CMemPool *mp = CMemPool::GetInstance();

    if (nullptr != mp)
        mp->Free(ptr);
}

void CBufChunkMgr::Destroy()
{
    bool bRet = false;
    BufChunkNode *pNode = m_lstChunk.GetHeader();

    while (nullptr != pNode)
    {
        bRet = m_lstChunk.Remove(pNode);

        if (!bRet)
            break;

        DeleteChunk(pNode);
        pNode = m_lstChunk.GetHeader();
    }
}

BufChunkNode *CBufChunkMgr::NewChunk()
{
    BufChunkNode *pNode = m_lstChunk.NewNode();

    if (nullptr == pNode)
    {
        printf("Error: NewNode\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return nullptr;
    }

    pNode->pStart = mmap(0, CHUNK_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);

    if (MAP_FAILED == pNode->pStart)
    {
        m_lstChunk.DeleteNode(pNode);
        printf("Error: mmap\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return nullptr;
    }

    pNode->nLength = CHUNK_SIZE;
    pNode->pCurPos = pNode->pStart;
    pNode->pEnd = (char *)pNode->pStart + pNode->nLength;

    return pNode;
}

void CBufChunkMgr::DeleteChunk(BufChunkNode *pNode)
{
    if (nullptr != pNode)
    {
        if (nullptr != pNode->pStart)
        {
            int iRet = munmap(pNode->pStart, pNode->nLength);

            if (0 != iRet)
                printf("Error: munmap error:%d\tfile=%s\tline=%d\n", errno, __FILE__, __LINE__);
        }

        m_lstChunk.DeleteNode(pNode);
    }
}

BufChunkNode *CBufChunkMgr::AddNewChunk()
{
    BufChunkNode *pNode = NewChunk();

    if (nullptr == pNode)
        return nullptr;

    if (!m_lstChunk.PushBack(pNode))
    {
        DeleteChunk(pNode);
        return nullptr;
    }

    return pNode;
}

void *CBufChunkMgr::Alloc(unsigned int nLen)
{
    void *ptr = nullptr;
    BufChunkNode *pTail = m_lstChunk.GetTailer();

    if (nullptr == pTail)
    {
        pTail = AddNewChunk();

        if (nullptr == pTail)
            return nullptr;
    }

    if ((char *)pTail->pEnd - (char *)pTail->pCurPos >= nLen)
    {
        ptr = pTail->pCurPos;
        pTail->pCurPos = (char *)pTail->pCurPos + nLen;
    }
    else
    {
        pTail = AddNewChunk();

        if (nullptr == pTail)
            return nullptr;

        if ((char *)pTail->pEnd - (char *)pTail->pCurPos >= nLen)
        {
            ptr = pTail->pCurPos;
            pTail->pCurPos = (char *)pTail->pCurPos + nLen;
        }
    }

    return ptr;
}